#include <string.h>
#include <malloc.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/**
 * 字符串表示形式
 *     1. 字符数组字符串
 *     2. 指针字符串
 * 区别 ：
 *     1. 指针字符串，被当作常量，不能修改字符串本身，而数组字符串，可以修改字符串本身；
 *     2. 指针字符串变量可以指向其它字符串，数组字符串变量不能指向另一个字符串；
 */
void test_str_create() {
    // 字符数组字符串，栈内存分配，栈内存释放，允许修改；
    char str1[] = "hello";
    str1[0] = 'A';
    printf("str1 = %s\n", str1);
//    str1 = "world"; // 直接报错，不能修改字符串本身

    // 指针字符串， 被当作字符常量，保存在常量区（不允许被修改），不能修改字符串本身，而数组字符串，可以修改字符串本身；
    char *str2 = "hello";
//    str2[0] = 'A';
//    printf("str2 = %s\n", str2); // 报错 ，Process finished with exit code 2816
    str2 = "world"; // 可以修改指针，指向其它字符串
    printf("str2 = %s\n", str2);
}

void test_str_fun() {
    printf("'hello'字符串长度 ：%lu\n", strlen("hello"));
    printf("------------ strcpy 复制字符串--------------\n");
    // 数组字符串复制
    char src[] = "hello";
    printf("str1 = %s\n", src);
//    // dst 小于 源字符串长度时，源字符串会发生意想不到改变
//    char dst[5];
//    strcpy(dst, src); // 参数是字符数组，不能用指针
//    printf("dst < src 时  src = %s(src发生了改变),dst = %s\n", src, dst);

    char dst[6];
    strcpy(dst, src); // 参数是字符数组，不能用指针
    printf("数组字符串复制 ：src = %s,dst = %s\n", src, dst);

    // 指针字符串复制
    char *src1 = "Hello";
    char *dst1 = (char *) malloc(6);
    strcpy(dst1, src1);
    printf("指针字符串复制 ：src = %s,dst = %s\n", src1, dst1);
    free(dst1);

    char *src2 = "World";
    char *dest2 = strdup(src2);  // 自动分配 + 复制
    printf("指针字符串复制 ：src2 = %s,dest2 = %s\n", src2, dest2);
    free(dest2);
    printf("----------- strncpy 复制前 n 个 字符---------------\n");
    char src3[] = "hello world";
    char *dest3 = (char *) malloc(6);
    // 复制前 5 个字符
    strncpy(dest3, src3, 5);
    printf("strncpy : src3 = %s,dest3 = %s\n", src3, dest3);
    free(dest3);
    printf("----------- strcat 拼接字符串---------------\n");
    char src4[] = "hello";
    printf("src4 地址 ：%p\n", &src4);
    char *dest4 = "world";
    // 拼接字符串
    strcat(src4, dest4);
    printf("连接后的 src4 地址 ：%p，src4 = %s\n", &src4, src4);
    printf("----------- strncmp 拼接前 n 个字符串---------------\n");
    char src5[] = "hello";
    printf("src5 地址 ：%p\n", &src5);
    char *dest5 = "world hahaha";
    // 拼接前 5 个字符
    strncat(src5, dest5, 5);
    printf("连接后的 src5 地址 ：%p，src5 = %s\n", &src5, src5);
    printf("----------- strcmp 比较字符串是否相同---------------\n");
    printf("compare1 = %d\n", strcmp("China", "Korea"));
    printf("compare2 = %d\n", strcmp("China", "China"));
    printf("----------- strlwr()/strupr() 大小写转换---------------\n");
    char src6[] = "AbdDefG";
    strlwr(src6);
    printf("大写转小写 ：%s\n", src6);
//    printf("大写转小写 ：%s\n", strlwr("AbdDefG")); // 返回指针,字面字符串是常量，不允许修改

//    char *dest6 = "AbdDefG";
//    strupr(dest6);
//    printf("小写转大写 ：%s", dest6); // 报错，应为 dest6 是常量，不允许修改。

    char dest6[] = "AbdDefG";
    printf("小写转大写 ：%s\n", strupr(dest6)); // 报错，应为 dest6 是常量，不允许修改。
}

/**
 * 字符串类型转换
 */
void test_str_type_conversion() {
    printf("------------ atoi/atof 字符串转数字--------------\n");
//    int a = atoi("123aaa") ; // 存在安全隐患
//    printf("atoi : %d,atof : %f\n", a, atof("12.34"));

    char input[] = "123aaa";
    char *endptr; // 指向转换失败的字符串
    int base = 10; // 进制，10进制，16进制，8进制
    long result = strtol(input, &endptr, base);
    // 错误检查
    if (endptr == input) {
        printf("错误：无有效数字\n");
    } else if (errno == ERANGE) {
        printf("数值超出 long 范围\n");
    } else if (*endptr != '\0') {
        printf("警告：部分转换，转换结果：%ld，剩余字符串：'%s'\n", result, endptr);          // 输出：-123
    } else {
        printf("转换结果：%ld\n", result);          // 输出：-123
    }

    printf("------------ sprintf 数字转字符串--------------\n");
    // 数字转字符串
    int a =12;
    float b = 12.34F;

    char *str = (char *) malloc(20);
    sprintf(str, "%d", a);
    printf("数字转字符串 ：%s\n", str);
    sprintf(str,"%.5f",b);
    printf("数字转字符串 ：%s\n", str);
}

void test_data() {
    time_t t ;
    time(&t);
    printf("当前时间戳 ：%ld\n", t);
    char * tStr = ctime(&t);
    printf("当前时间 ：%s\n", tStr);

    // 格式化输出
    // 1. localtime 将时间戳转换为struct tm结构体，线程不安全的
    struct tm *tInfo = localtime(&t);
    // 2. strftime 将struct tm结构体转换为字符串
    char buffer[80];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tInfo);
    // strftime(buffer, sizeof(buffer), "%a %b %d %T %Y", timeinfo); // 星期 月名 日 时间 年
    // strftime(buffer, sizeof(buffer), "%x %X", timeinfo);          // 本地日期/时间格式
    printf("格式化时间：%s\n", buffer);
}
/**
 * 数学函数
 *  - double exp(double x) ：返回 e 的 x 次幂的值。
 *  - double log(double x) ：返回 x 的自然对数（基数为 e 的对数）
 *  - double pow(double x, double y) ：返回 x 的 y 次幂。
 *  - double sqrt(double x) ：返回 x 的平方根。
 *  - double fabs(double x) ：返回 x 的绝对值。
 */
void test_math() {

}
/**
 *
 *  栈内存布局
 *    +------------------+
 *    |     栈(stack)    |  ← 局部变量、函数参数
 *    +------------------+
 *    |                  |
 *    |                  |
 *    |                  |
 *    +------------------+
 *    |     堆(heap)     |  ← malloc/new动态分配
 *    +------------------+
 *    |    .bss段        |  ← 未初始化静态/全局变量
 *    +------------------+
 *    |    .data段       |  ← 已初始化静态/全局变量
 *    +------------------+
 *    |    代码段(text)   |  ← 程序指令
 *    +------------------+
 */


void test_void() {
    // void 指针 ： 只有内存块的地址信息，没有类型信息；等同于无类型指针，可以指向任意类型的数据
    // void 指针与其他所有类型指针之间是`互相转换关系`，任一类型的指针都可以转为 void 指针，而 void 指针也可以转为任一类型的指针；
    int x = 10;
    void *p = &x; // 整数指针转为 void 指针
//    printf("%c\n", *p); // 报错，由于不知道 void 指针指向什么类型的值，所以不能用 * 运算符取出它指向的值。
    int *q = p; // void 指针转为整数指针


}
// 动态分配内存
void test_malloc(){
    printf("----------- malloc 动态分配内存---------------\n");
    // 动态申请数组
    int *p1 = NULL;
    // malloc() : 分配内存，若错误则返回NULL
    p1 = (int *)malloc(5 * sizeof(int));
    if (p1 == NULL) {
        printf("内存分配失败\n");
        return;
    }
    for (int i = 0; i < 5; i++){
        p1[i] = i * 5;
    }
    printf("数组的内容 ：");
    for (int i = 0; i < 5; ++i) {
        printf(" %d,", p1[i]);
    }
    printf("\n");
    free(p1);
    p1 = NULL;
    printf("----------- calloc 动态分配内存---------------\n");
    // calloc ：分配内存
    int *p2 = NULL;
    // 分配 5个 int 大小的连续空间
    p2 = (int *)calloc(5 , sizeof(int));
    if (p2 == NULL) {
        printf("内存分配失败\n");
        return;
    }
    for (int i = 0; i < 5; i++){
        p2[i] = i * 5;
    }
    printf("数组的内容 ：");
    for (int i = 0; i < 5; ++i) {
        printf(" %d,", p2[i]);
    }
    printf("\n");
    free(p2);
    p2 = NULL;
    printf("----------- realloc 重新分配内存---------------\n");
    // realloc ：重新分配内存，调整先前通过 malloc/calloc 分配的内存大小
    int * p3 =  (int *)malloc(sizeof(int) * 5);
    int * p4 = (int *)realloc(p3, sizeof(int) * 10);

    for (int i = 0; i < 10; i++){
        p4[i] = i * 5;
    }
    printf("数组的内容 ：");
    for (int i = 0; i < 10; ++i) {
        printf(" %d,", p4[i]);
    }
    printf("\n");
    free(p4);
    p4 = NULL;
//    free(p3); // 报错
    p3 = NULL;
}
int main() {
//    test_str_create();
//    test_str_fun();
//    test_str_type_conversion();
//    test_data();
//    test_math();
//    test_void();
    test_malloc();
}